package amtf

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"time"

	"github.com/pkg/errors"
	"gorm.io/datatypes"
)

var A插件代码文件夹 = "aa"
var A插件cfg文件夹 = filepath.Join(Acfg文件夹, "chajian")
var A插件model文件夹 = "aa_model"

func A存json(data []any, 插件s []*S插件) (err error) {
	for i, 插件 := range 插件s {
		json文件名 := 插件.A名称 + ".json"
		filePath := filepath.Join(A插件cfg文件夹, json文件名)
		file, err := os.Create(filePath)
		if err != nil {
			return fmt.Errorf("创建 JSON 文件时出错: %w", err)
		}
		defer file.Close()
		bytes, _ := json.Marshal(data[i])
		_, err = file.Write(bytes)
		if err != nil {
			return fmt.Errorf("写入 JSON 文件时出错: %w", err)
		}
	}
	return
}

func A存json_单个废(bytes []byte) (err error) {
	filePath := filepath.Join(A插件cfg文件夹, "开发.json")
	file, err := os.Create(filePath)
	if err != nil {
		return fmt.Errorf("创建 JSON 文件时出错: %w", err)
	}
	defer file.Close()
	_, err = file.Write(bytes)
	if err != nil {
		return fmt.Errorf("写入 JSON 文件时出错: %w", err)
	}
	return
}

// func A成插件s7() (err error) {
// 	A予插件_基础部分(插件)
// 	A成插件文件(插件)
// 	for _, 组件 := range 插件.A组件s {
// 		A成组件文件(插件, 组件)
// 	}
// 	return
// }

func A成插件s7(插件s []*S插件) (err error) {
	文件夹 := filepath.Join(A插件代码文件夹)
	A成模板文件(文件夹, "aa插件集手动部分.go.tpl", "aa插件集手动部分", 插件s)

	for _, 插件 := range 插件s {
		A插组件初始化(插件)
	}
	A予插件_基础部分(插件s)

	for _, 插件 := range 插件s {
		if err := A成插件s7_单个(插件); err != nil {
			return err
		}
	}
	return
}

func A成插件s7_单个(插件 *S插件) (err error) {
	// A予插件_基础部分(插件)
	A成插件文件(插件)
	for _, 组件 := range 插件.A组件s {
		A成组件文件(插件, 组件)
	}
	return
}

func A成插件给前端_从数据库() map[string]any {
	// 插件结构体 := A插件集["开发"].A组件s[0]
	// models, _ := 插件结构体.Aservice.A查_加载所有关联项()

	// 插组件集 := make(map[string]any)
	// 插组件集["models"] = models
	// // for key, value := range A插件集 {
	// // 	插组件集[key] = value
	// // }
	// // for key, value := range A组件集 {
	// // 	插组件集[key] = value
	// // }
	// return 插组件集

	插组件集 := make(map[string]any)
	插组件集["插件集"] = A插件集
	插组件集["组件集"] = A组件集
	插组件集["字段集"] = A字段集
	return 插组件集
}

func A成插件给前端(插件名s []string) map[string]any {
	// 直接把全局 A插件集 给前端的话 json.Marshal 可能会因为 包含 函数字段而 转换报错?
	// struct标签里面 json:- 就解决了😓
	插组件集 := make(map[string]any)
	插组件集["插件集"] = A插件集
	插组件集["组件集"] = A组件集
	插组件集["字段集"] = A字段集
	// for key, value := range A插件集 {
	// 	插组件集[key] = value
	// }
	// for key, value := range A组件集 {
	// 	插组件集[key] = value
	// }
	return 插组件集
}

func A取插件() *S插件 {
	插件 := &S插件{}
	jsonDataBytes := A取json("老鱼商店.json")
	json.Unmarshal(jsonDataBytes, 插件)
	return 插件
}

func A予插件_开发json(json文件 string) *S插件 {
	// 开发这个插件，只能是从json构建 才方便用gorm的关联查询，如果直接查数据库，难以构成父子结构体
	插件 := &S插件{}
	jsonDataBytes := A取json(json文件)
	json.Unmarshal(jsonDataBytes, 插件)
	A予插件_基础部分_单个(插件)
	return 插件
}

func A取插件s_从数据库(插件 *S插件) []*S插件 {
	// 下面的组件是指的 aa\dev\chajian\插件.go
	组件 := 插件.A组件s[0]
	service := Sservice{}
	service.A组件 = 组件
	组件.Aservice = &service

	models, _ := 组件.Aservice.A查_加载所有关联项()
	jsonDataBytes2, _ := json.Marshal(models)

	插件s := []*S插件{}
	_ = json.Unmarshal(jsonDataBytes2, &插件s)
	return 插件s
}

// 同时登记插组件集，后面注册插组件时，插组件之间可能会相互引用
func A取插件s(插件名s []string) []*S插件 {
	// filenames := []string{"老鱼商店.json"}
	// filenames = append(filenames, "项目管理.json")
	插件s := []*S插件{}
	for _, 插件名 := range 插件名s {
		插件 := &S插件{}
		jsonDataBytes := A取json(插件名)
		json.Unmarshal(jsonDataBytes, 插件)
		插件s = append(插件s, 插件)
		A插件集[插件.A名称] = 插件
		A插组件初始化(插件)
		// for _, 组件 := range 插件.A组件s {
		// 	组件.A插件名 = 插件.A名称
		// 	A成表名(组件)
		// 	组件.A自动挡hook = &S组件自动挡hook{}
		// 	A组件集[A成组件id(插件.A名称, 组件.A名称)] = 组件
		// }
	}
	return 插件s
}

func A插组件初始化(插件 *S插件) {
	for _, 组件 := range 插件.A组件s {
		组件.A插件名 = 插件.A名称
		组件.A插件英文名 = 插件.A英文名
		A成表名(组件)
		A成model名_go(组件)

		组件.A自动挡hook = &S组件自动挡hook{}
		A组件集[A成组件id(插件.A名称, 组件.A名称)] = 组件
	}
}

func A成插件文件(插件 *S插件) {
	文件夹 := filepath.Join(A插件代码文件夹, 插件.A英文名)
	A成模板文件(文件夹, "插件.go.tpl", 插件.A名称, 插件)
}

func A成组件文件(插件 *S插件, 组件 *S组件) {
	组件.A插件名 = 插件.A名称
	文件夹 := filepath.Join(A插件代码文件夹, 插件.A英文名, 组件.A英文名)
	A成模板文件(文件夹, "组件.go.tpl", 组件.A名称, 组件)
	if 组件.A有自动挡hook {
		A成模板文件(文件夹, "自动挡hook.go.tpl", 组件.A名称+"自动挡hook", 组件)
	}
	if 组件.A有手动挡 {
		A成模板文件(文件夹, "手动挡.go.tpl", 组件.A名称+"手动挡", 组件)
	}

	// 文件夹 = filepath.Join(A插件model文件夹, 组件.A插件名)
	文件夹 = filepath.Join(A插件model文件夹)
	A成模板文件(文件夹, "model.go.tpl", 组件.Amodel名, 组件)
}

func A含json否(str string) bool {
	re := regexp.MustCompile(`arr|图片|富文本`)
	return re.MatchString(str)
}

func A成模板文件(文件夹 string, 模板名 string, 文件名 string, 插件或组件或插件s any) {
	fmt.Printf("插件或组件信息: %+v\n", 插件或组件或插件s)
	// A字段组 := A取字段值(插件或组件或插件s, "A字段组")
	// fmt.Printf("A字段组 👉 %+v\n", A字段组)

	os.MkdirAll(文件夹, 0755)

	文件全名 := filepath.Join(文件夹, fmt.Sprintf("%s.go", 文件名))

	// 文件已经存在👇
	if _, err := os.Stat(文件全名); err == nil {
		if A含其中之一(文件名, "hook|手动挡", false) {
			return
		}
		// 获取当前时间毫秒数
		// now := time.Now().UnixNano() / 1e6
		now := time.Now().Format("2006-01-02 15-04.000")
		backupDir := filepath.Join(fmt.Sprintf("删除保留/%s", now), A插件代码文件夹)
		os.MkdirAll(backupDir, 0755)

		relPath, _ := filepath.Rel(A插件代码文件夹, 文件全名)
		backupFilePath := filepath.Join(backupDir, relPath)
		// 创建备份文件所在目录
		备份目录 := filepath.Dir(backupFilePath)
		os.MkdirAll(备份目录, 0755)
		// 转移文件
		os.Rename(文件全名, backupFilePath)
	} else {
		// fmt.Printf("FileInfo 👉 %+v\n", FileInfo)
		fmt.Printf("err 👉 %+v\n", err)
	}
	outputFile, _ := os.Create(文件全名)

	tplPath := filepath.Join("amtf", "template", 模板名)
	tplContent, _ := os.ReadFile(tplPath)
	funcMap := template.FuncMap{
		"解json": 解json,
	}
	// tpl, err := template.New("example").Funcs(funcMap).Parse(tplContent)
	tpl, _ := template.New("插件或组件或插件s").Option("missingkey=zero").Funcs(funcMap).Parse(string(tplContent))
	defer outputFile.Close()
	err := tpl.Execute(outputFile, 插件或组件或插件s)
	if err != nil {
		fmt.Printf("err 👉 %+v\n", err)
	}
}

// 将 datatypes.JSON 转换为 Go 语言中 datatypes.JSON 字面量格式的字符串
func 解json(data datatypes.JSON) map[string]any {
	var result map[string]any
	err := json.Unmarshal([]byte(data), &result)
	if err != nil {
		return nil
	}
	return result
}

// func A成组件(json文件 string, 插件 *S插件) (组件 *S组件) {
// 	jsonDataBytes := A取json(json文件)
// 	组件 = &S组件{}
// 	err := json.Unmarshal(jsonDataBytes, 组件)
// 	if err != nil {
// 		_ = fmt.Errorf("解析 JSON 文件失败: %w", err)
// 	}
// 	model := A成model(组件, 插件)
// 	组件.Amodel = model
// 	return
// }

func A取json(relativePath string) (jsonDataBytes []byte) {
	jsonFilePath := filepath.Join(A插件cfg文件夹, relativePath)
	jsonFile, err := os.Open(jsonFilePath)
	// fmt.Printf("jsonFilePath 👉 %+v\n", jsonFilePath)
	APrintf("jsonFilePath 👉 %+v", jsonFilePath)
	fmt.Printf("err 👉 %+v\n", err)
	if err != nil {
		// return fmt.Errorf("打开 JSON 文件失败: %w", err)
		errors.Wrapf(err, "[jsonFilePath:%s] 出错!", jsonFilePath)
	}
	defer jsonFile.Close()

	// 读取文件内容
	jsonDataBytes, err = io.ReadAll(jsonFile)
	if err != nil {
		_ = fmt.Errorf("读取 JSON 文件内容失败: %w", err)
	}
	return
}

// 用反射动态生成的model不符合要求？字段名显示为 internal/abi.A名称 {Bytes: *3} 不符合gorm首字母大写的要求？
func A成model_反射方式(组件 *S组件, 插件 *S插件) any {
	字段s := 组件.A字段组
	fields := make([]reflect.StructField, 0, len(字段s))

	for _, 字段 := range 字段s {
		var 字段类型 reflect.Type
		switch 字段.A类型 {
		case "string":
			字段类型 = reflect.TypeOf("")
		case "int":
			字段类型 = reflect.TypeOf(0)
		case "float64":
			字段类型 = reflect.TypeOf(0.0)
		// 可以根据需要添加更多类型
		default:
			fmt.Printf("不支持的类型: %s\n", 字段.A类型)
			continue
		}

		field := reflect.StructField{
			// Name:    "A" + 字段.A名称,
			Name:    字段.A名称go,
			Type:    字段类型,
			Tag:     reflect.StructTag(fmt.Sprintf(`json:"%s" gorm:"column:%s;备注:;"`, 字段.A名称, 字段.A名称)),
			PkgPath: "",
		}
		fields = append(fields, field)
	}

	structType := reflect.StructOf(fields)
	instance := reflect.New(structType).Elem()
	kk := instance.Interface()
	return kk
}
